/**
 *
 */
package de.cbf.cam.dsa.facet.weapon_cc;

import java.beans.IndexedPropertyChangeEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;

import de.cbf.cam.dsa.facet.weapon_cc.helper.WeaponCC_CombatValues;
import de.cbf.util.condition.ConditionDescription;
import de.cbf.util.condition_test.entities.ConditionResult;
import de.cbf.util.condition_test.entities.IValueProviderForConditionTest;
import de.cbf.util.entity.facet.constants.ConstantsFontColorLogView;
import de.cbf.util.entity.facet.entities.FacetOfEntity;
import de.cbf.util.entity.facet.util.TextPartStyled;
import de.cbf.util.uuidheader.entities.UUIDHeader;
import de.cbf.util.uuidheader.entities.UUIDReference;
import de.cbf.util.uuidheader.exceptions.EntityMergeException;

/**
 * @author Nebelritter
 *
 */
@XmlRootElement(name="weapon_cc")
@XmlAccessorType(XmlAccessType.FIELD)
public class FacetWeaponCC extends FacetOfEntity implements FacetWeaponCCConstants{
	
	protected Double 		fLength;
	protected Integer 		fBf;

	/*** uuid of talent and the combat values	 */
	protected Map<UUIDReference, WeaponCC_CombatValues> 	fTalentValuesMap;
	/** reference to talent and condition to use talent	 */
	protected Map<UUIDReference, ConditionDescription>		fTalent2ConditionMap;
	/**reference to maneuver and condition to use it	 */
	protected Map<UUIDReference, ConditionDescription>		fManeuver2ConditionMap;
	@XmlTransient
	protected Map<UUIDReference, PropertyChangeListener>	talentPCLMap;
	
	@Override
	protected void init() {
		super.init();
		fLength = UNSET_LENGTH;
		fBf 	= UNSET_BF;
		
		fTalentValuesMap		= new HashMap<UUIDReference, WeaponCC_CombatValues>();		
		fTalent2ConditionMap	= new HashMap<UUIDReference, ConditionDescription>();
		fManeuver2ConditionMap		= new HashMap<UUIDReference, ConditionDescription>();
		
		talentPCLMap = new HashMap<UUIDReference, PropertyChangeListener>();
	}
	
	@Override
	protected void doMergeDataFrom(UUIDHeader entity, boolean i_am_older)
			throws EntityMergeException {
		super.doMergeDataFrom(entity, i_am_older);
		if (entity instanceof FacetWeaponCC) {
			FacetWeaponCC other = (FacetWeaponCC) entity;
			if(i_am_older){
				this.fBf = other.fBf;
				this.fLength = other.fLength;
				this.fTalentValuesMap.clear();
				Set<UUIDReference> keys = other.fTalentValuesMap.keySet();
				for (UUIDReference key : keys) {
					WeaponCC_CombatValues value = other.fTalentValuesMap.get(key);
					try {
						WeaponCC_CombatValues clonedValue = (WeaponCC_CombatValues) value.clone();
						this.fTalentValuesMap.put(key, clonedValue);
					} catch (CloneNotSupportedException e) {
						throw new EntityMergeException(e);
					}
				}
				this.fTalent2ConditionMap.clear();
				keys = other.fTalent2ConditionMap.keySet();
				for (UUIDReference key : keys) {
					ConditionDescription value = other.fTalent2ConditionMap.get(key);
					try {
						ConditionDescription clonedValue = (ConditionDescription) value.clone();
						this.fTalent2ConditionMap.put(key, clonedValue);
					} catch (CloneNotSupportedException e) {
						throw new EntityMergeException(e);
					}
				}
				this.fManeuver2ConditionMap.clear();
				keys = other.fManeuver2ConditionMap.keySet();
				for (UUIDReference key : keys) {
					ConditionDescription value = other.fManeuver2ConditionMap.get(key);
					try {
						ConditionDescription clonedValue = (ConditionDescription) value.clone();
						this.fManeuver2ConditionMap.put(key, clonedValue);
					} catch (CloneNotSupportedException e) {
						throw new EntityMergeException(e);
					}
				}
			}
		}else{
			String mergeEntityClass = entity == null? "null":entity.getClass().getName();
			String errorMsg ="EntityToMerge "+mergeEntityClass+" does not fit class:"+this.getClass().getName();
			throw new EntityMergeException(errorMsg);
		}
	}
	
	@Override
	public List<TextPartStyled> getFacetStyledDescription() {
		
		List<TextPartStyled> textStyleInfo = new ArrayList<TextPartStyled>();
		
		TextPartStyled textPart;
			
		//weaponCC heading
		textPart = new TextPartStyled();
		textPart.setText(TEXT_LOG_HEADING_WEAPON_CC+"\n");
		textPart.setStyleConstant(ConstantsFontColorLogView.STYLE_FACET);		
		textStyleInfo.add(textPart );
		
		//fLength
		textPart = new TextPartStyled();
		textPart.setText("\t"+TEXT_LOG_LENGTH);
		textPart.setStyleConstant(ConstantsFontColorLogView.STYLE_CATEGORY);		
		textStyleInfo.add(textPart );
		//fLength value
		textPart = new TextPartStyled();
		textPart.setText(fLength+" \n");
		textPart.setStyleConstant(ConstantsFontColorLogView.STYLE_VALUE);		
		textStyleInfo.add(textPart );

		//fBf 
		textPart = new TextPartStyled();
		textPart.setText("\t"+TEXT_LOG_BF);
		textPart.setStyleConstant(ConstantsFontColorLogView.STYLE_CATEGORY);		
		textStyleInfo.add(textPart );
		//fBf value
		textPart = new TextPartStyled();
		textPart.setText(fBf+" \n");
		textPart.setStyleConstant(ConstantsFontColorLogView.STYLE_VALUE);		
		textStyleInfo.add(textPart );

		//talent values
		textPart = new TextPartStyled();
		textPart.setText("\t"+TEXT_LOG_TALENTS_VALUES+"\n");
		textPart.setStyleConstant(ConstantsFontColorLogView.STYLE_CATEGORY);		
		textStyleInfo.add(textPart );
		//talent values value
		textStyleInfo.addAll(buildTalentValuesLog(fTalentValuesMap));
		
		//talent conditions
		textPart = new TextPartStyled();
		textPart.setText("\t"+TEXT_LOG_TALENTS_CONDITIONS+"\n");
		textPart.setStyleConstant(ConstantsFontColorLogView.STYLE_CATEGORY);		
		textStyleInfo.add(textPart );
		//talent values value
		textStyleInfo.addAll(buildTalentConditionsLog(fTalent2ConditionMap));
		
		//maneuver conditions
		textPart = new TextPartStyled();
		textPart.setText("\t"+TEXT_LOG_MANEUVER_CONDITIONS+"\n");
		textPart.setStyleConstant(ConstantsFontColorLogView.STYLE_CATEGORY);		
		textStyleInfo.add(textPart );
		//maneuver conditions value
		textStyleInfo.addAll(buildManeuverConditionsLog(fManeuver2ConditionMap));
		return textStyleInfo;
	}
	
	protected List<TextPartStyled> buildTalentValuesLog(Map<UUIDReference, WeaponCC_CombatValues> talentValuesMap) {
		List<TextPartStyled> parts = new ArrayList<TextPartStyled>();
		
		TextPartStyled textPart;
		//build table header 
		StringBuffer buffer = new StringBuffer();
		buffer.append("\t\t\t\t");
		buffer.append(TEXT_LOG_TP);
		buffer.append("\t\t\t");
		buffer.append(TEXT_LOG_TP_KK);
		buffer.append("\t");
		buffer.append(TEXT_LOG_INI);
		buffer.append("\t");
		buffer.append(TEXT_LOG_WM);
		buffer.append("\t");
		buffer.append(TEXT_LOG_DK);
		buffer.append("\t\t");
		buffer.append(TEXT_LOG_SPECIALS);
		buffer.append("\n");
		
		textPart = new TextPartStyled();
		textPart.setText(buffer.toString());
		textPart.setStyleConstant(ConstantsFontColorLogView.STYLE_CATEGORY);
		parts.add(textPart);
		
		Set<UUIDReference> talents = talentValuesMap.keySet();
		for (UUIDReference talent : talents) {
			textPart = new TextPartStyled();
			textPart.setText("\t\t"+talent.getSavedName()+"\n");
			textPart.setStyleConstant(ConstantsFontColorLogView.STYLE_CATEGORY);		
			parts.add(textPart);
			
			WeaponCC_CombatValues values = talentValuesMap.get(talent);
			String valueString = values.getLogDescription();
			textPart = new TextPartStyled();
			textPart.setText(valueString);
			textPart.setStyleConstant(ConstantsFontColorLogView.STYLE_VALUE);		
			parts.add(textPart );			
		}
		return parts;
	}
	
	protected Collection<? extends TextPartStyled> buildTalentConditionsLog(
			Map<UUIDReference, ConditionDescription> fTalent2ConditionMap) {
		List<TextPartStyled> resultList = new ArrayList<TextPartStyled>();
		Set<UUIDReference> keys = fTalent2ConditionMap.keySet();
		for (UUIDReference key : keys) {
			TextPartStyled textPartKey = new TextPartStyled();
			textPartKey.setText("\t\t"+key.getSavedName()+": \n");
			textPartKey.setStyleConstant(ConstantsFontColorLogView.STYLE_CATEGORY);
			resultList.add(textPartKey);
			ConditionDescription condition = fTalent2ConditionMap.get(key);
			List<TextPartStyled> valueList = condition.getFacetStyledDescription();
			resultList.addAll(valueList);
		}
		return resultList;
	}
	protected Collection<? extends TextPartStyled> buildManeuverConditionsLog(
			Map<UUIDReference, ConditionDescription> fManeuver2ConditionMap) {
		List<TextPartStyled> resultList = new ArrayList<TextPartStyled>();
		Set<UUIDReference> keys = fManeuver2ConditionMap.keySet();
		for (UUIDReference key : keys) {
			TextPartStyled textPartKey = new TextPartStyled();
			textPartKey.setText("\t\t"+key.getSavedName()+": \n");
			textPartKey.setStyleConstant(ConstantsFontColorLogView.STYLE_CATEGORY);
			resultList.add(textPartKey);
			ConditionDescription condition = fManeuver2ConditionMap.get(key);
			List<TextPartStyled> valueList = condition.getFacetStyledDescription();
			resultList.addAll(valueList);
		}
		return resultList;
	}
	
	@Override
	public String hasUnsetValues() {
		StringBuffer result = new StringBuffer();
		if(UNSET_LENGTH.equals(fLength)){
			result.append(TEXT_UNSET_LENGTH+"\n");
		}
		if(UNSET_BF.equals(fBf)){
			result.append(TEXT_UNSET_BF+"\n");
		}
		if(fTalentValuesMap.isEmpty()){
			result.append(TEXT_UNSET_TALENT_VALUES+"\n");
		}else{
			Set<UUIDReference> keys = fTalentValuesMap.keySet();			
			for (UUIDReference key : keys) {
				WeaponCC_CombatValues combatValues = fTalentValuesMap.get(key);
				String unsetValues = combatValues.hasUnsetValues();
				if(unsetValues != null && ! unsetValues.isEmpty()){
					result.append(key.getSavedName()+"\n");
					result.append("\t"+unsetValues);
				}
				
			}
		}
		return result.toString();
	}

	/* (non-Javadoc)
	 * @see de.cbf.util.entity.facet.entities.FacetOfEntity#setDeleted(java.lang.Long)
	 */
	@Override
	public void setDeleted(Long aDeleted) {		
		super.setDeleted(aDeleted);
		Collection<UUIDReference> keys = fTalentValuesMap.keySet();
		for (UUIDReference key : keys) {
			WeaponCC_CombatValues value = fTalentValuesMap.get(key);
			if(value != null){
				value.setDeleted(aDeleted);
			}
			key.setDeleted(aDeleted);			
		}
	}
	
	public WeaponCC_CombatValues getWeaponValuesByTalent(UUIDReference talent){
		return fTalentValuesMap.get(talent);
	}

	public void setWeaponValuesByTalent(UUIDReference talent,WeaponCC_CombatValues values){
		fTalentValuesMap.put(talent, values);
		PropertyChangeListener pcl = new TalentValuesPCL(talent);
		values.getPropertyChangeSupport().addPropertyChangeListener(pcl);
		talentPCLMap.put(talent, pcl);
		propertyChangeSupport.fireIndexedPropertyChange(PN_TALENTS_VALUES, PI_TALENTS_VALUES, null, fTalentValuesMap);
	}

	public void removeWeaponValuesByTalent(UUIDReference talentReference){	
		if(talentReference == null) return;
		//get values to remove pcl from
		WeaponCC_CombatValues values = fTalentValuesMap.get(talentReference);
		//get pcl for talent
		PropertyChangeListener pcl = talentPCLMap.get(talentReference);
		//remove pcl
		values.getPropertyChangeSupport().removePropertyChangeListener(pcl);
		//remove mappings
		fTalentValuesMap.remove(talentReference);
		talentPCLMap.remove(talentReference);
		//fire PCE
		propertyChangeSupport.fireIndexedPropertyChange(PN_TALENTS_VALUES, PI_TALENTS_VALUES, null, fTalentValuesMap);
	}
	
	public UUIDReference getTalentReferenceForUUID(UUID savedUUID) {
		Set<UUIDReference> keys = fTalentValuesMap.keySet();
		for (UUIDReference uuidReference : keys) {
			if(uuidReference.getSavedUuid().toString().equals(savedUUID.toString())){
				return uuidReference;
			}
		}
		return null;
	}
	
	public List<UUIDReference> getTalents(){
		Set<UUIDReference> keys = fTalentValuesMap.keySet();
		List<UUIDReference> resultList = new ArrayList<UUIDReference>(keys);		
		return resultList;
	}
	
	public Map<UUIDReference, ConditionDescription> getTalentConditions(){
		return new HashMap<UUIDReference, ConditionDescription>(fTalent2ConditionMap);
	}
	
	public void setTalentConditions(Map<UUIDReference, ConditionDescription> talentConditions){
		fTalent2ConditionMap.clear();
		fTalent2ConditionMap.putAll(talentConditions);
		propertyChangeSupport.fireIndexedPropertyChange(PN_TALENT_CONDITIONS, PI_TALENT_CONDTITIONS, null, fTalent2ConditionMap);
	}
		
	public void addTalentCondition(UUIDReference talentReference, ConditionDescription condition){
		fTalent2ConditionMap.put(talentReference, condition);
		propertyChangeSupport.fireIndexedPropertyChange(PN_TALENT_CONDITIONS, PI_TALENT_CONDTITIONS, null, fTalent2ConditionMap);
	}
	
	public void removeTalentCondition(UUIDReference talentReference){
		fTalent2ConditionMap.remove(talentReference);
		propertyChangeSupport.fireIndexedPropertyChange(PN_TALENT_CONDITIONS, PI_TALENT_CONDTITIONS, null, fTalent2ConditionMap);
	}
	
	public List<UUIDReference> getTalentsPossible(IValueProviderForConditionTest conditionValueProviders){
		List<UUIDReference> resultList = new ArrayList<UUIDReference>();
		Set<UUIDReference> keys = fTalent2ConditionMap.keySet();
		for (UUIDReference key : keys) {
			ConditionDescription condition = fTalent2ConditionMap.get(key);
			ConditionResult conditionResult = condition.testCondition(conditionValueProviders);
			Boolean fullfilled = conditionResult.isConditionFullfilled();
			if(fullfilled){
				resultList.add(key);
			}
		}		
		return resultList;
	}
	
	public Boolean isTalentPossible(UUID talentUUID, IValueProviderForConditionTest conditionValueProviders){
		Boolean result = false;
		Set<UUIDReference> keys = fTalent2ConditionMap.keySet();
		for (UUIDReference key : keys) {
			if(key.hasUUID(talentUUID)){
				ConditionDescription condition = fTalent2ConditionMap.get(key);
				ConditionResult conditionResult = condition.testCondition(conditionValueProviders);
				result = conditionResult.isConditionFullfilled();
			}
		}		
		return result;
	}

	public Map<UUIDReference, ConditionDescription> getManeuverCondition(){
		return new HashMap<UUIDReference, ConditionDescription>(fManeuver2ConditionMap);
	}
	
	public void setManeuverCondition(Map<UUIDReference, ConditionDescription> maneuverConditions){
		fManeuver2ConditionMap.clear();
		fManeuver2ConditionMap.putAll(maneuverConditions);
		propertyChangeSupport.fireIndexedPropertyChange(PN_MANEUVER_CONDITIONS, PI_MANEUVER_CONDTITIONS, null, fManeuver2ConditionMap);
	}
	
	public void addManeuverCondition(UUIDReference maneuverReference, ConditionDescription condition){
		fManeuver2ConditionMap.put(maneuverReference, condition);
		propertyChangeSupport.fireIndexedPropertyChange(PN_MANEUVER_CONDITIONS, PI_MANEUVER_CONDTITIONS, null, fManeuver2ConditionMap);
	}
	
	public void removeManeuverCondition(UUIDReference maneuverReference){
		fManeuver2ConditionMap.remove(maneuverReference);
		propertyChangeSupport.fireIndexedPropertyChange(PN_MANEUVER_CONDITIONS, PI_MANEUVER_CONDTITIONS, null, fManeuver2ConditionMap);
	}
	
	public Double getLength() {
		return fLength;
	}

	public void setLength(Double newLength) throws IllegalArgumentException {
		if(fLength != null && fLength.equals(newLength)){
            return;
        }
        Double oldLength = fLength;
        fLength = newLength;
        propertyChangeSupport.fireIndexedPropertyChange(PN_LENGTH, PI_LENGTH, oldLength, fLength);
	}

	public Integer getBf() {
		return fBf;
	}

	public void setBf(Integer newBf) throws IllegalArgumentException {
		Integer oldBf = this.fBf;
		fBf = newBf;
		propertyChangeSupport.fireIndexedPropertyChange(PN_BF, PI_BF,
				oldBf,fBf);
	}

	public String getTp(UUIDReference talent) {
		WeaponCC_CombatValues values = fTalentValuesMap.get(talent);
		if(values != null){
			return values.getTp();
		}
		return null;
	}

	public void setTp(UUIDReference talent,String newTP) throws IllegalArgumentException {
		WeaponCC_CombatValues values = fTalentValuesMap.get(talent);
		if(values != null){
			values.setTp(newTP);
		}
	}

	public Integer getTpKK_Base(UUIDReference talent) {
		WeaponCC_CombatValues values = fTalentValuesMap.get(talent);
		if(values != null){
			return values.getTpKK_Base();
		}
		return null;
	}

	public void setTpKK_Base(UUIDReference talent,Integer newTpKK_Base) throws IllegalArgumentException{
		WeaponCC_CombatValues values = fTalentValuesMap.get(talent);
		if(values != null){
			values.setTpKK_Base(newTpKK_Base);
		}
	}

	public Integer getTpKK_Step(UUIDReference talent) {
		WeaponCC_CombatValues values = fTalentValuesMap.get(talent);
		if(values != null){
			return values.getTpKK_Step();
		}
		return null;
	}

	public void setTpKK_Step(UUIDReference talent,Integer newTpKK_Step) throws IllegalArgumentException{
		WeaponCC_CombatValues values = fTalentValuesMap.get(talent);
		if(values != null){
			values.setTpKK_Step(newTpKK_Step);
		}
	}

	public Integer getIni(UUIDReference talent) {
		WeaponCC_CombatValues values = fTalentValuesMap.get(talent);
		if(values != null){
			return values.getIni();
		}
		return null;
	}

	public void setIni(UUIDReference talent,Integer newIni) throws IllegalArgumentException{
		WeaponCC_CombatValues values = fTalentValuesMap.get(talent);
		if(values != null){
			values.setIni(newIni);
		}
	}

	public Integer getWmAT(UUIDReference talent) {
		WeaponCC_CombatValues values = fTalentValuesMap.get(talent);
		if(values != null){
			return values.getWmAT();
		}
		return null;
	}

	public void setWmAT(UUIDReference talent,Integer newWmAT) throws IllegalArgumentException{
		WeaponCC_CombatValues values = fTalentValuesMap.get(talent);
		if(values != null){
			values.setWmAT(newWmAT);
		}
	}

	public Integer getWmPA(UUIDReference talent) {
		WeaponCC_CombatValues values = fTalentValuesMap.get(talent);
		if(values != null){
			return values.getWmPA();
		}
		return null;
	}

	public void setWmPA(UUIDReference talent,Integer newWmPA) throws IllegalArgumentException{
		WeaponCC_CombatValues values = fTalentValuesMap.get(talent);
		if(values != null){
			values.setWmPA(newWmPA);
		}
	}

	public Integer getDkBits(UUIDReference talent) {
		WeaponCC_CombatValues values = fTalentValuesMap.get(talent);
		if(values != null){
			return values.getDkBits();
		}
		return null;
	}

	public void setDkBits(UUIDReference talent,Integer newDkBits) throws IllegalArgumentException{
		WeaponCC_CombatValues values = fTalentValuesMap.get(talent);
		if(values != null){
			values.setDkBits(newDkBits);
		}
	}

	public Integer getSpecialBits(UUIDReference talent) {
		WeaponCC_CombatValues values = fTalentValuesMap.get(talent);
		if(values != null){
			return values.getSpecialBits();
		}
		return null;
	}

	public void setSpecialBits(UUIDReference talent,Integer newSpecialBits) throws IllegalArgumentException{
		WeaponCC_CombatValues values = fTalentValuesMap.get(talent);
		if(values != null){
			values.setSpecialBits(newSpecialBits);
		}
	}


	protected class TalentValuesPCL implements PropertyChangeListener{
		protected UUIDReference talent;

		public TalentValuesPCL(UUIDReference talent) {
			super();
			this.talent = talent;
		}

		/* (non-Javadoc)
		 * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
		 */
		@Override
		public void propertyChange(PropertyChangeEvent evt) {
			if (evt instanceof IndexedPropertyChangeEvent) {
				IndexedPropertyChangeEvent idxEvt = (IndexedPropertyChangeEvent) evt;
				Object[] newValues = new Object[2];
				newValues[ARRAY_IDX_TALENT] = talent;
				newValues[ARRAY_IDX_VALUE] = evt.getNewValue();
				propertyChangeSupport.fireIndexedPropertyChange(idxEvt.getPropertyName(), idxEvt.getIndex(),
						idxEvt.getOldValue(), newValues);
			}else{
				Object[] newValues = new Object[2];
				newValues[ARRAY_IDX_TALENT] = talent;
				newValues[ARRAY_IDX_VALUE] = evt.getNewValue();
				propertyChangeSupport.firePropertyChange(evt.getPropertyName(),evt.getOldValue(),newValues);
			}
		}
	}


	
}
